#include "stdafx.h"
#include "Quad.h"
#include "Camera2D.h"

namespace a2d
{
//------------------------------------------------------------------

	static const char quad_shader_vs[] =
		"#version 330 core\n"
		"\n"
		"uniform mat4 PVW;\n"
		"uniform vec4 color;\n"
		"uniform vec2 sizequad;\n"
		"uniform float depth;\n"
		"uniform float scale;\n"
		"\n"
		"layout (location = 0) in vec2 in_position;\n"
		"layout (location = 1) in vec2 in_tex_coord;\n"
		"\n"
		"out vec2 tex_coord;\n"
		"out vec4 v_color;\n"
		"\n"
		"void main(void)\n"
		"{\n"
		"	vec2 npos = in_position*mat2( sizequad.x*scale, 0.0,\n"
		"								  0.0, sizequad.y*scale);\n"
		"	gl_Position = PVW * (vec4(npos, depth, 1.0));\n"
		"	tex_coord = in_tex_coord;\n"
		"	v_color = color;\n"
		"}\n"
		;

	static const char quad_shader_fs[] =
		"#version 330 core\n"
		"\n"
		"uniform sampler2D tex;\n"
		"\n"
		"in vec2 tex_coord;\n"
		"in vec4 v_color;\n"
		"\n"
		"out vec4 outColor;\n"
		"\n"
		"void main(void)\n"
		"{\n"
		"	vec4 texel = texture(tex, tex_coord);\n"
		"	if(texel.a < 0.01) discard;\n"
		"//	if (texel.r>=0.99 && texel.g>=0.99 && texel.b>=0.99) discard;\n"
		"	outColor = texel;\n"
		"}\n"
		;

	VAO Quad::m_vao;
	ShaderProgram Quad::m_shaders;

	bool Quad::Create(const glm::vec2 &size)
	{
		m_size = size;

		if ( !m_vao.Create() )
			return false;
		m_vao.Bind();		

		m_vbo.Create();
		m_vbo.Bind();
		const GLfloat quad_data[] =
		{
			0.0f, 0.0f,		0.0f, 0.0f,
			1.0f, 0.0f,		1.0f, 0.0f,
			1.0f, 1.0f,		1.0f, 1.0f,
			0.0f, 1.0f,		0.0f, 1.0f
		};
		m_vbo.SetData(sizeof(quad_data), quad_data);
		m_vbo.SetVertexAtribs(2, 2, 4);

		if ( !m_shaders.Create(quad_shader_vs, quad_shader_fs) )
			return false;
				
		m_uniform_pvw = m_shaders.SetUniform("PVW", Camera2D::Get().GetProjViewMatrix());
		m_uniform_color = m_shaders.SetUniform("color", m_color);
		m_uniform_size = m_shaders.SetUniform("sizequad", m_size);
		m_uniform_depth = m_shaders.SetUniform("depth", m_depth);
		m_uniform_scale = m_shaders.SetUniform("scale", m_scale);
		
		return true;
	}


	void Quad::SetDepth(float d)
	{
		if ( m_depth == d )
			return;
		m_depth = d;
		m_shaders.UpdateUniform(m_uniform_depth, m_depth);
	}


	void Quad::SetSize(const glm::vec2 &size)
	{
		if ( m_size == size )
			return;
		m_size = size;
		m_shaders.UpdateUniform(m_uniform_size, m_size);
	}

	void Quad::SetTexRect(const glm::vec4 &rect)
	{
		const glm::vec4 trect(	((float)rect.x + 0.5f) / m_size.x,
								((float)rect.y + 0.5f) / m_size.y,
								((float)rect.z + 0.5f) / m_size.x,
								((float)rect.w + 0.5f) / m_size.y );

		if ( m_rect == trect )
			return;
		m_rect = trect;
		SetFlip(false, false);
	}

	void Quad::SetFlip(bool hor, bool ver)
	{
		float x1, x2, y1, y2;


		if ( !hor && !ver )
		{
			x1 = m_rect.x;
			y1 = m_rect.y;
			x2 = m_rect.z;
			y2 = m_rect.w;
		}
		else if ( hor && !ver )
		{
			x1 = m_rect.z;
			y1 = m_rect.y;
			x2 = m_rect.x;
			y2 = m_rect.w;
		}
		else if ( !hor && ver )
		{
			x1 = m_rect.x;
			y1 = m_rect.w;
			x2 = m_rect.z;
			y2 = m_rect.y;
		}
		else
		{
			x1 = m_rect.z;
			y1 = m_rect.w;
			x2 = m_rect.x;
			y2 = m_rect.y;
		}

		const GLfloat quad_data[] =
		{
			0.0f, 0.0f, x1, y1,
			1.0f, 0.0f, x2, y1,
			1.0f, 1.0f, x2, y2,
			0.0f, 1.0f, x1, y2
		};
		m_vbo.SetData(sizeof(quad_data), quad_data);
	}

	void Quad::Draw(const glm::vec2 &pos)
	{
		m_vao.Bind();
		m_shaders.Bind();
		
		if ( (m_pos != pos) || Camera2D::Get().IsUpdate() )
		{
			m_pos = pos;
			
			m_shaders.UpdateUniform(m_uniform_pvw, Camera2D::Get().GetProjViewMatrix()*glm::translate(glm::vec3(m_pos.x, m_pos.y, 0.0f)));
		}
		
		glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
	}

	void Quad::Destroy()
	{
		m_vbo.Destroy();
		m_shaders.Destroy();
		m_vao.Destroy();
	}

//------------------------------------------------------------------
} // namespace a2d
